﻿using System;
using System.Windows;
using NHibernate;
using NHibernate.Validator.Cfg;
using NHibernate.Validator.Engine;
using Ninject.Conditions;
using Ninject.Core.Behavior;
using Ninject.Core.Parameters;
using TestBat.IModels.Domain;
using TestBat.IModels.Factories;
using TestBat.IModels.Models;
using TestBat.IModels.Repositories;
using TestBat.IModels.Services;
using TestBat.ISettings;
using TestBat.ISettings.Database;
using TestBat.Models.Domain;
using TestBat.Models.Exceptions;
using TestBat.Models.Factories;
using TestBat.Models.Interceptors.Session;
using TestBat.Models.Models;
using TestBat.Models.Repositories;
using TestBat.Models.Services;
using uNhAddIns.CastleAdapters;
using uNhAddIns.SessionEasier;
using uNhAddIns.SessionEasier.Conversations;

namespace TestBat.GuyWire.Configurators
{
    public class ModelModule : Ninject.Core.StandardModule
    {
        public override void Load()
        {
            // bind NHV configuration
            Bind<ValidatorEngine>().ToMethod<ValidatorEngine>(ctx =>
            {
                var ve = new ValidatorEngine();
                ve.Configure();
                return ve;
            }).Using<SingletonBehavior>();

            // bind factories
            Bind<IEntityFactory>().To<EntityFactory>().Using<SingletonBehavior>();
            Bind<ITaskFactory>().To<TaskFactory>().Using<SingletonBehavior>();
            Bind<ITaskFolderFactory>().To<TaskFolderFactory>().Using<SingletonBehavior>();
            Bind<IScheduleFactory>().To<ScheduleFactory>().Using<SingletonBehavior>();
            Bind<ITraineeGroupFactory>().To<TraineeGroupFactory>().Using<SingletonBehavior>();

            // bind data-binding interceptor
            Bind<SessionIntercepter>().ToMethod<SessionIntercepter>(ctx =>
            {
                var interceptor = new SessionIntercepter(Kernel.Get<IEntityFactory>());
                return interceptor;
            }).Using<SingletonBehavior>();

            // bind NHibernate
            Bind<IConfigurationProvider>().ToMethod<DefaultSessionFactoryConfigurationProvider>(ctx =>
                {
                    var nhConfigurator = new DefaultSessionFactoryConfigurationProvider();
                    nhConfigurator.BeforeConfigure += (sender, e) =>
                    {
                        // get application context (web, console or exe) HACK!
                        var application = Application.Current;
                        string connectionString = "";
                        if (application == null)
                        {
                            var console = ctx.Parameters.Get<VariableParameter>("console");
                            if (console != null) // console
                            {
                            }
                            else // web
                            {
                                IWebSettingsPersister settingsPersister = Kernel.Get<IWebSettingsPersister>();
                                IWebSettings settings = settingsPersister.Load();
                                connectionString = "Server=" + settings.DBAddress + ";Initial Catalog=" + settings.DBName + ";User Id=" + settings.DBUsername + ";Password=" + settings.DBPassword;
                                e.Configuration.Properties.Add("connection.connection_string", connectionString);
                                IDatabaseType type = Kernel.Get<IDatabaseTypeConverter>().Convert(settings.DBType);
                                e.Configuration.Properties.Add("dialect", type.Dialect);
                                e.Configuration.Properties.Add("connection.driver_class", type.Driver);
                            }
                        }
                        else // exe
                        {
                            IExeSettingsPersister settingsPersister = Kernel.Get<IExeSettingsPersister>();
                            IExeSettings settings = settingsPersister.Load();
                            connectionString = "Server=" + settings.DBAddress + ";Initial Catalog=" + settings.DBName + ";User Id=" + settings.DBUsername + ";Password=" + settings.DBPassword;
                            e.Configuration.Properties.Add("connection.connection_string", connectionString);
                            IDatabaseType type = Kernel.Get<IDatabaseTypeConverter>().Convert(settings.DBType);
                            e.Configuration.Properties.Add("dialect", type.Dialect);
                            e.Configuration.Properties.Add("connection.driver_class", type.Driver);
                        }
                        // END HACK
                    };
                    nhConfigurator.AfterConfigure += (sender, e) =>
                    {
                        SessionIntercepter sessionIntercepter = Kernel.Get<SessionIntercepter>();
                        e.Configuration.SetInterceptor(sessionIntercepter);
                        ValidatorInitializer.Initialize(e.Configuration, Kernel.Get<ValidatorEngine>());
                    };
                    return nhConfigurator;
                }
                ).Using<SingletonBehavior>();

            Bind<ISessionFactoryProvider>().ToMethod<SessionFactoryProvider>(ctx =>
                {
                    return new SessionFactoryProvider(Kernel.Get<IConfigurationProvider>());
                }
                ).Using<SingletonBehavior>();

            Bind<ISessionFactory>().ToMethod<ISessionFactory>(ctx =>
                {
                    try
                    {
                        return Kernel.Get<ISessionFactoryProvider>().GetFactory(null);
                    }
                    catch (Exception e)
                    {
                        throw new DatabaseException(e);
                    }
                });
            Bind<IConversationFactory>().To<DefaultConversationFactory>().Using<SingletonBehavior>();
            Bind<ISessionWrapper>().To<SessionWrapper>();
            Bind<IConversationsContainerAccessor>().ToMethod<NhConversationsContainerAccessor>(e =>
                {
                    return new NhConversationsContainerAccessor(Kernel.Get<ISessionFactoryProvider>());
                }
                ).Using<SingletonBehavior>();

            // bind repositories
            Bind<ITaskRepository>().To<TaskRepository>();
            Bind<ITaskFolderRepository>().To<TaskFolderRepository>();
            Bind<IScheduleRepository>().To<ScheduleRepository>();
            Bind<ITraineeGroupRepository>().To<TraineeGroupRepository>();
            Bind<ITraineeRepository>().To<TraineeRepository>();
            Bind<ITestRepository>().To<TestRepository>();
            Bind<ITestTaskRepository>().To<TestTaskRepository>();

            // bind models
            Bind<ITaskModel>().To<TaskModel>().Using<SingletonBehavior>();
            Bind<ITaskFolderModel>().To<TaskFolderModel>().Using<SingletonBehavior>();
            Bind<IScheduleModel>().To<ScheduleModel>().Using<SingletonBehavior>();
            Bind<ITraineeGroupModel>().To<TraineeGroupModel>().Using<SingletonBehavior>();
            Bind<ITraineeModel>().To<TraineeModel>().Using<SingletonBehavior>();
            Bind<ITestModel>().To<TestModel>().Using<SingletonBehavior>();
            Bind<ITestTaskModel>().To<TestTaskModel>().Using<SingletonBehavior>();

            // bind domain models
            Bind<ILeafTaskFolder>().ToMethod<LeafTaskFolder>(ctx => Kernel.Get<IEntityFactory>().Create<LeafTaskFolder>());
            Bind<ILeafTaskFolder>().ToMethod<LeafTaskFolder>(ctx =>
                {
                    LeafTaskFolder ltf = Kernel.Get<IEntityFactory>().Create<LeafTaskFolder>();
                    ltf.Name = "NewFolder";
                    return ltf;
                }).Only(When.Context.HasVariable("manual"));

            Bind<IEssayTask>().ToMethod<EssayTask>(ctx => Kernel.Get<IEntityFactory>().Create<EssayTask>());
            Bind<IEssayTask>().ToMethod<EssayTask>(ctx =>
                {
                    EssayTask t = Kernel.Get<IEntityFactory>().Create<EssayTask>();
                    t.Question = "";
                    t.Name = "New Essaytask";
                    t.MaximumPoints = 0;
                    return t;
                }).Only(When.Context.HasVariable("manual"));
            Bind<IMultipleChoiceTask>().ToMethod<MultipleChoiceTask>(ctx => Kernel.Get<IEntityFactory>().Create<MultipleChoiceTask>());
            Bind<IMultipleChoiceTask>().ToMethod<MultipleChoiceTask>(ctx =>
            {
                MultipleChoiceTask t = Kernel.Get<IEntityFactory>().Create<MultipleChoiceTask>();
                t.MaximumPoints = 0;
                t.Question = "";
                t.Name = "New multiple choice task";
                return t;
            }).Only(When.Context.HasVariable("manual"));
            Bind<IMultipleResponseTask>().ToMethod<MultipleResponseTask>(ctx => Kernel.Get<IEntityFactory>().Create<MultipleResponseTask>());
            Bind<IMultipleResponseTask>().ToMethod<MultipleResponseTask>(ctx =>
            {
                MultipleResponseTask t = Kernel.Get<IEntityFactory>().Create<MultipleResponseTask>();
                t.MaximumPoints = 0;
                t.Question = "";
                t.Grading = MultipleResponseTaskGrading.All;
                t.Name = "New multiple response task";
                return t;
            }).Only(When.Context.HasVariable("manual"));
            Bind<IChoice>().ToMethod<Choice>(ctx => Kernel.Get<IEntityFactory>().Create<Choice>());
            Bind<IChoice>().ToMethod<Choice>(ctx =>
            {
                Choice c = Kernel.Get<IEntityFactory>().Create<Choice>();
                c.Correct = false;
                c.Text = "New choice";
                return c;
            }).Only(When.Context.HasVariable("manual"));
            Bind<ITestChoice>().ToMethod<TestChoice>(ctx => Kernel.Get<IEntityFactory>().Create<TestChoice>());

            Bind<ISchedule>().ToMethod<Schedule>(ctx =>
            {
                Schedule s = Kernel.Get<IEntityFactory>().Create<Schedule>();
                s.Name = "New schedule";
                s.Start = DateTime.Now;
                s.End = DateTime.Now;
                s.Introduction = "";
                return s;
            }).Only(When.Context.HasVariable("manual"));
            Bind<IScheduleTraineeGroup>().ToMethod<ScheduleTraineeGroup>(ctx => Kernel.Get<IEntityFactory>().Create<ScheduleTraineeGroup>());

            Bind<ISchedule>().ToMethod<Schedule>(ctx => Kernel.Get<IEntityFactory>().Create<Schedule>());

            Bind<ITraineeGroup>().ToMethod<TraineeGroup>(ctx => Kernel.Get<IEntityFactory>().Create<TraineeGroup>());
            Bind<ITraineeGroup>().ToMethod<TraineeGroup>(ctx =>
            {
                TraineeGroup tg = Kernel.Get<IEntityFactory>().Create<TraineeGroup>();
                tg.Name = "New trainee group";
                return tg;
            }).Only(When.Context.HasVariable("manual"));

            Bind<ITrainee>().ToMethod<Trainee>(ctx => Kernel.Get<IEntityFactory>().Create<Trainee>());
            Bind<ITrainee>().ToMethod<Trainee>(ctx =>
            {
                Trainee t = Kernel.Get<IEntityFactory>().Create<Trainee>();
                t.Username = "New trainee";
                t.Password = "Password";
                return t;
            }).Only(When.Context.HasVariable("manual"));

            Bind<ITest>().ToMethod<Test>(ctx => Kernel.Get<IEntityFactory>().Create<Test>());
            Bind<ITestEssayTask>().ToMethod<TestEssayTask>(ctx => Kernel.Get<IEntityFactory>().Create<TestEssayTask>());
            Bind<ITestMultipleChoiceTask>().ToMethod<TestMultipleChoiceTask>(ctx => Kernel.Get<IEntityFactory>().Create<TestMultipleChoiceTask>());
            Bind<ITestMultipleResponseTask>().ToMethod<TestMultipleResponseTask>(ctx => Kernel.Get<IEntityFactory>().Create<TestMultipleResponseTask>());

            Bind<IEssaySolution>().ToMethod<EssaySolution>(ctx => Kernel.Get<IEntityFactory>().Create<EssaySolution>());
            Bind<IMultipleChoiceSolution>().ToMethod<MultipleChoiceSolution>(ctx => Kernel.Get<IEntityFactory>().Create<MultipleChoiceSolution>());
            Bind<IMultipleResponseSolution>().ToMethod<MultipleResponseSolution>(ctx => Kernel.Get<IEntityFactory>().Create<MultipleResponseSolution>());

            Bind<IScheduleResults>().To<ScheduleResults>();
            Bind<ITestResults>().To<TestResults>();

            // bind services
            Bind<IUnsavedChangesService>().To<UnsavedChangesService>().Using<SingletonBehavior>();
            Bind<IConversationService>().To<ConversationService>().Using<SingletonBehavior>();
            Bind<IResultsService>().To<ResultsService>().Using<SingletonBehavior>();

            // bind conversation
            Bind<IConversation>().ToMethod<IConversation>(f =>
            {
                if (convId == null)
                {
                    convId = Guid.NewGuid().ToString();
                }
                IConversation c = Kernel.Get<IConversationsContainerAccessor>().Container.Get(convId) ?? Kernel.Get<IConversationFactory>().CreateConversation(convId);
                Kernel.Get<IConversationsContainerAccessor>().Container.SetAsCurrent(c);

                return c;
            });
        }

        private static string convId;
    }
}
